return {
	"neovim/nvim-lspconfig",
	event = { "BufReadPre", "BufNewFile" },
	dependencies = {
		"williamboman/mason.nvim",
		"williamboman/mason-lspconfig.nvim",
		"nvimdev/lspsaga.nvim",
	},
	config = function()
		require("mason").setup({
			ui = {
				icons = {
					package_installed = "✓",
					package_pending = "➜",
					package_uninstalled = "✗",
				},
			},
		})

		require("lspsaga").setup({})

		require("mason-lspconfig").setup({
			-- A list of servers to automatically install if they're not already installed
			ensure_installed = {
				"pylsp",
				"lua_ls",
				"rust_analyzer",
				"tsserver",
				"cssmodules_ls",
				"emmet_language_server",
				"jdtls",
			},
		})

		-- Set different settings for different languages' LSP
		-- LSP list: https://github.com/neovim/nvim-lspconfig/blob/master/doc/server_configurations.md
		-- How to use setup({}): https://github.com/neovim/nvim-lspconfig/wiki/Understanding-setup-%7B%7D
		--     - the settings table is sent to the LSP
		--     - on_attach: a lua callback function to run after LSP atteches to a given buffer
		local lspconfig = require("lspconfig")

		-- Customized on_attach function
		-- See `:help vim.diagnostic.*` for documentation on any of the below functions
		local opts = { noremap = true, silent = true }
		vim.keymap.set("n", "<space>e", vim.diagnostic.open_float, opts)
		vim.keymap.set("n", "[d", vim.diagnostic.goto_prev, opts)
		vim.keymap.set("n", "]d", vim.diagnostic.goto_next, opts)
		vim.keymap.set("n", "<space>q", vim.diagnostic.setloclist, opts)

		-- Use an on_attach function to only map the following keys
		-- after the language server attaches to the current buffer
		local on_attach = function(_, bufnr)
			-- Enable completion triggered by <c-x><c-o>
			vim.api.nvim_buf_set_option(bufnr, "omnifunc", "v:lua.vim.lsp.omnifunc")

			-- See `:help vim.lsp.*` for documentation on any of the below functions
			local bufopts = { noremap = true, silent = true, buffer = bufnr }
			vim.keymap.set("n", "gD", vim.lsp.buf.declaration, bufopts)
			vim.keymap.set("n", "gd", vim.lsp.buf.definition, bufopts)
			vim.keymap.set("n", "K", "<cmd>Lspsaga hover_doc<CR>", bufopts)
			vim.keymap.set("n", "gi", vim.lsp.buf.implementation, bufopts)
			-- vim.keymap.set('n', '<C-k>', vim.lsp.buf.signature_help, bufopts)
			-- vim.keymap.set('n', '<space>wa', vim.lsp.buf.add_workspace_folder, bufopts)
			-- vim.keymap.set('n', '<space>wr', vim.lsp.buf.remove_workspace_folder, bufopts)
			-- vim.keymap.set('n', '<space>wl', function()
			-- print(vim.inspect(vim.lsp.buf.list_workspace_folders()))
			-- end, bufopts)
			-- vim.keymap.set('n', '<space>D', vim.lsp.buf.type_definition, bufopts)
			vim.keymap.set("n", "<space>rn", "<cmd>Lspsaga rename<CR>", bufopts)
			vim.keymap.set("n", "<space>ca", "<cmd>Lspsaga code_action<CR>", bufopts)
			vim.keymap.set("n", "gr", "<cmd>Lspsaga finder<CR>", bufopts)
			-- vim.keymap.set("n", "<space>f", function()
			-- vim.lsp.buf.format({ async = true })
			-- end, bufopts)
		end

		-- Configure each language
		-- How to add LSP for a specific language?
		-- 1. use `:Mason` to install corresponding LSP
		-- 2. add configuration below

		local capabilities = require("cmp_nvim_lsp").default_capabilities()

		lspconfig.pylsp.setup({
			capabilities = capabilities,
			on_attach = on_attach,
		})

		lspconfig.tsserver.setup({
			capabilities = capabilities,
			filetypes = {
				"javascript",
				"javascriptreact",
				"javascript.jsx",
				"typescript",
				"typescriptreact",
				"typescript.tsx",
			},
			on_attach = on_attach,
		})

		lspconfig.lua_ls.setup({
			capabilities = capabilities,
			filetypes = { "lua" },
			on_attach = on_attach,
		})

		lspconfig.cssmodules_ls.setup({
			capabilities = capabilities,
			on_attach = on_attach,
		})

		lspconfig.emmet_language_server.setup({
			capabilities = capabilities,
			on_attach = on_attach,
		})

		lspconfig.jdtls.setup({
			capabilities = capabilities,
			on_attach = on_attach,
		})
	end,
}
